/*
 * idt.c - Mokri
 * Contributors: xMine
 * (Any new Contributor may add himself/herself to the list)
 * Changes and fixes to be made:
 * Source:
 */

#include "idt.h"
#include "gdt.h"

struct IDT_Eintrag idt[256];
struct IDT_Pointer idtp;

// Interrupt Funktionen ...
extern void interrupt_stub_0 ();
extern void interrupt_stub_1 ();
extern void interrupt_stub_2 ();
extern void interrupt_stub_3 ();
extern void interrupt_stub_4 ();
extern void interrupt_stub_5 ();
extern void interrupt_stub_6 ();
extern void interrupt_stub_7 ();
extern void interrupt_stub_error_code_8 ();
extern void interrupt_stub_9 ();
extern void interrupt_stub_error_code_10 ();
extern void interrupt_stub_error_code_11 ();
extern void interrupt_stub_error_code_12 ();
extern void interrupt_stub_error_code_13 ();
extern void interrupt_stub_error_code_14 ();
extern void interrupt_stub_15 ();
extern void interrupt_stub_16 ();
extern void interrupt_stub_error_code_17 ();
extern void interrupt_stub_18 ();
extern void interrupt_stub_19 ();
extern void interrupt_stub_20 ();
// 21 - 31 sind unbelegt
extern void interrupt_stub_32 ();
extern void interrupt_stub_33 ();
extern void interrupt_stub_34 ();
extern void interrupt_stub_35 ();
extern void interrupt_stub_36 ();
extern void interrupt_stub_37 ();
extern void interrupt_stub_38 ();
extern void interrupt_stub_39 ();
extern void interrupt_stub_40 ();
extern void interrupt_stub_41 ();
extern void interrupt_stub_42 ();
extern void interrupt_stub_43 ();
extern void interrupt_stub_44 ();
extern void interrupt_stub_45 ();
extern void interrupt_stub_46 ();
extern void interrupt_stub_47 ();
extern void interrupt_stub_162 (); // System Call

// Fehlernachrichten
char exception_msg[20][40] = {
	"Division By Zero",
	"Debug Exception",
	"Non Maskable Interrupt Exception",
	"Breakpoint Exception",
	"Into Detected Overflow Exception",
	"Out of Bounds Exception",
	"Invalid Opcode Exception",
	"No Coprocessor Exception",
	"Double Fault Exception",
	"Coprocessor Segment Overrun Exception",
	"Bad TSS Exception",
	"Segment Not Present Exception",
	"Stack Fault Exception",
	"General Protection Fault Exception",
	"Page Fault Exception",
	"Unknown Interrupt Exception",
	"Coprocessor Fault Exception",
	"Alignment Check Exception",
	"Machine Check Exception",
	"Unknown Exception"
};

// Dieses Array zeigt, was getan werden soll bei einer Exception.
#define EFLAG_NOTHING	0  // Nichts machen
#define EFLAG_MESSAGE	1  // Meldung ausgeben
#define EFLAG_KILL	2  // Verursacher abschiessen
#define EFLAG_COREDUMP	4  // Coredump des Verursachers ausgeben
#define EFLAG_ALLOC	8  // Sonderfall für Page Fault
#define EFLAG_PANIC	16 // Kernel Panic auslösen

char exception_what_to_do[20] = {
	EFLAG_MESSAGE | EFLAG_KILL,		// Div by Zero
	EFLAG_NOTHING,				// Debug
	EFLAG_PANIC,				// unmaskable interrupt
	EFLAG_NOTHING,				// Breakpoint
	EFLAG_MESSAGE | EFLAG_KILL,		// Into detected overflow (??)
	EFLAG_MESSAGE | EFLAG_KILL,		// Out of Bounds
	EFLAG_MESSAGE | EFLAG_KILL,		// invalid opcode
	EFLAG_PANIC,				// No coprocessor (wtf?)
	EFLAG_PANIC | EFLAG_COREDUMP,		// Double Fault
	EFLAG_PANIC | EFLAG_COREDUMP,		// Coprocessor segment overrun
	EFLAG_PANIC,				// bad TTS
	EFLAG_PANIC,				// segment not present
	EFLAG_MESSAGE | EFLAG_KILL,		// stack fault
	EFLAG_MESSAGE | EFLAG_KILL,		// general protection fault
	EFLAG_ALLOC,				// page fault
	EFLAG_MESSAGE | EFLAG_KILL,		// unknown interrupt
	EFLAG_PANIC,				// coprocessor fault
	EFLAG_MESSAGE | EFLAG_KILL,		// alignment
	EFLAG_PANIC,				// machine check
	EFLAG_PANIC				// unknown
};

void irq_map () {
	// Master-PIC initialisieren
	outb(0x20, 0x01|0x10); // Initialisierungsbefehl fuer den PIC
	outb(0x21, 0x20); // Interruptnummer fuer IRQ 0
	outb(0x21, 0x04); // An IRQ 2 haengt der Slave
	outb(0x21, 0x01); // ICW 4

	// Slave-PIC initialisieren
	outb(0xa0, 0x01|0x10); // Initialisierungsbefehl fuer den PIC
	outb(0xa1, 0x28); // Interruptnummer fuer IRQ 8
	outb(0xa1, 0x02); // An IRQ 2 haengt der Slave
	outb(0xa1, 0x01); // ICW 4

	// Alle IRQs aktivieren (demaskieren)
	outb(0x21, 0x0);
	outb(0xa1, 0x0);
}

void irq_settick (int hz) {
	int teiler = 1193180 / hz;	// Teiler berechnen
	outb (0x43, 0x36);		// Kommando Byte senden
	outb (0x40, teiler & 0xFF);	// Low  Byte des Teilers senden
	outb (0x40, teiler >> 8);	// High Byte des Teilers senden
}

void idt_load () {
	asm volatile("lidt %0" : : "m" (idtp));
}

void idt_set_gate (unsigned char num, unsigned long base, unsigned short sel, unsigned char flags) {
	idt[num].basis_low = (unsigned short) base;
	idt[num].basis_high= (unsigned short)(base>>16);
	idt[num].garbage = (char)0x0;
	idt[num].kSegment = sel;
	idt[num].flags = flags;
}

void idt_init () {
	memset (idt, 0x0, sizeof(idt));
	idtp.limit = (sizeof (struct IDT_Eintrag) * 256) - 1; // Größe berechnen
	idtp.basis = (unsigned int)&idt;

	// Hier funktionen mit idt_set_gate adden
	// Exceptions
	idt_set_gate (0, (unsigned)interrupt_stub_0, 0x08, 0x8E);
	idt_set_gate (1, (unsigned)interrupt_stub_1, 0x08, 0x8E);
	idt_set_gate (2, (unsigned)interrupt_stub_2, 0x08, 0x8E);
	idt_set_gate (3, (unsigned)interrupt_stub_3, 0x08, 0x8E);
	idt_set_gate (4, (unsigned)interrupt_stub_4, 0x08, 0x8E);
	idt_set_gate (5, (unsigned)interrupt_stub_5, 0x08, 0x8E);
	idt_set_gate (6, (unsigned)interrupt_stub_6, 0x08, 0x8E);
	idt_set_gate (7, (unsigned)interrupt_stub_7, 0x08, 0x8E);
	idt_set_gate (8, (unsigned)interrupt_stub_error_code_8, 0x08, 0x8E);
	idt_set_gate (9, (unsigned)interrupt_stub_9, 0x08, 0x8E);
	idt_set_gate (10, (unsigned)interrupt_stub_error_code_10, 0x08, 0x8E);
	idt_set_gate (11, (unsigned)interrupt_stub_error_code_11, 0x08, 0x8E);
	idt_set_gate (12, (unsigned)interrupt_stub_error_code_12, 0x08, 0x8E);
	idt_set_gate (13, (unsigned)interrupt_stub_error_code_13, 0x08, 0x8E);
	idt_set_gate (14, (unsigned)interrupt_stub_error_code_14, 0x08, 0x8E);
	idt_set_gate (15, (unsigned)interrupt_stub_15, 0x08, 0x8E);
	idt_set_gate (16, (unsigned)interrupt_stub_16, 0x08, 0x8E);
	idt_set_gate (17, (unsigned)interrupt_stub_error_code_17, 0x08, 0x8E);
	idt_set_gate (18, (unsigned)interrupt_stub_18, 0x08, 0x8E);
	idt_set_gate (19, (unsigned)interrupt_stub_19, 0x08, 0x8E);
	// IRQs
	idt_set_gate (32, (unsigned)interrupt_stub_32, 0x08, 0x8E);
	idt_set_gate (33, (unsigned)interrupt_stub_33, 0x08, 0x8E);
	idt_set_gate (34, (unsigned)interrupt_stub_34, 0x08, 0x8E);
	idt_set_gate (35, (unsigned)interrupt_stub_35, 0x08, 0x8E);
	idt_set_gate (36, (unsigned)interrupt_stub_36, 0x08, 0x8E);
	idt_set_gate (37, (unsigned)interrupt_stub_37, 0x08, 0x8E);
	idt_set_gate (38, (unsigned)interrupt_stub_38, 0x08, 0x8E);
	idt_set_gate (39, (unsigned)interrupt_stub_39, 0x08, 0x8E);
	idt_set_gate (40, (unsigned)interrupt_stub_40, 0x08, 0x8E);
	idt_set_gate (41, (unsigned)interrupt_stub_41, 0x08, 0x8E);
	idt_set_gate (42, (unsigned)interrupt_stub_42, 0x08, 0x8E);
	idt_set_gate (43, (unsigned)interrupt_stub_43, 0x08, 0x8E);
	idt_set_gate (44, (unsigned)interrupt_stub_44, 0x08, 0x8E);
	idt_set_gate (45, (unsigned)interrupt_stub_45, 0x08, 0x8E);
	idt_set_gate (46, (unsigned)interrupt_stub_46, 0x08, 0x8E);
	idt_set_gate (47, (unsigned)interrupt_stub_47, 0x08, 0x8E);
	// System Call Int
	idt_set_gate (162, (unsigned)interrupt_stub_162, 0x08, 0x8E);

	idt_load (); // IDT laden
	irq_map (); // IRQs neu mappen
	irq_settick (TICKS_PER_SECOND); // IRQ0 einrichten
	asm volatile ("sti"); // IRQs aktivieren
}

// Wird aufgerufen sobald ein Interrupt gefeuert wird
// Natürlich hier schon mit Ring 0 (Kernel) Rechten :)
struct stack_frame * idt_behandlung (struct stack_frame *sf) {
	struct stack_frame *new_sf = sf;

	if (sf->interrupt >= 0 && sf->interrupt <= 0x1f) {
		/* Exceptions */
		char *errptr;
		char command;
		if (sf->interrupt < 19) {
			errptr = exception_msg[sf->interrupt];
			command = exception_what_to_do[sf->interrupt];
		} else {
			errptr = exception_msg[19];
			command = exception_what_to_do[sf->interrupt];
		}
		
		if (command & EFLAG_MESSAGE) {
			printf ("%c\nKritischer Fehler im Programm mit der PID %u:\n%s\n",
				(char)(14+COLOR_RED), GetRunningTaskPID(), errptr);
		}
		
		if (command & EFLAG_COREDUMP) {
			printf ("\n === CORE DUMP (PID: %u) ===\n", GetRunningTaskPID());
			dump_process_core (sf);
		}
		
		if (command & EFLAG_ALLOC) { // Sonderfall für Page fault
			paging_page_fault (sf);
		}
		
		if (command & EFLAG_KILL) {
			DestroyTask (GetRunningTaskPID(), -1);
		}
		
		if (command & EFLAG_PANIC) {
			kernel_panic (errptr);
		}

	    } else if (sf->interrupt >= 0x20 && sf->interrupt <= 0x2f) {
		/* IRQ */

		if (sf->interrupt == 0x20) { // Timer
			new_sf = irq_schedule (sf); // Scheduler aufrufen
			tss[1] = (uint32_t) (new_sf + 1);
		} else if (sf->interrupt == 0x21) { // Tastatur
			keyboard_handler ();
		} else if (sf->interrupt == 0x26) { // Floppy
			floppy_handler ();
		}

		// Ggf. PIC B sagen, dass der IRQ abgearbeitet wurde
		if (sf->interrupt >= 0x28) {
			outb (0xa0, 0x20);
		}
		outb (0x20, 0x20); // PIC A benachrichtigen

	    } else if (sf->interrupt == 162) {
		/* Systemaufruf */
		syscall_handler (sf, GetTaskStruct (GetRunningTaskPID()));
	    }

	    return new_sf;
}
